package com.tbit.uqbike.client.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.tbit.common.constant.AppMenuConstant;
import com.tbit.common.constant.OperationPlatform;
import com.tbit.common.entity.UserOpLog;
import com.tbit.common.utils.IpUtil;
import com.tbit.uqbike.client.business.user_op_log.service.UserOpLogService;
import com.tbit.uqbike.client.config.RefundSwitchConfig;
import com.tbit.uqbike.client.constant.*;
import com.tbit.uqbike.client.dao.core.UserToAgentDao;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.pojo.dto.RefundMoneyReturnDTO;
import com.tbit.uqbike.client.pojo.newEntity.AccountConfigExt;
import com.tbit.uqbike.client.pojo.newEntity.UniAppMenu;
import com.tbit.uqbike.client.pojo.newEntity.UserOtherBound;
import com.tbit.uqbike.client.pojo.newEntity.UserSta;
import com.tbit.uqbike.client.pojo.vo.UserCardVO;
import com.tbit.uqbike.client.pojo.vo.UserLogoutValidVO;
import com.tbit.uqbike.client.remote.tenxun.models.DetectAuthResponse;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.service.impl.UserServiceImpl;
import com.tbit.uqbike.client.util.*;
import com.tbit.uqbike.client.util.http.WyydLoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.LogManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
 * 用户
 *
 * @author Leon
 * 2017年2月24日 下午5:51:05
 */
@Controller
@RequestMapping("/user")
@Slf4j
public class UserController {
    private final static org.apache.logging.log4j.Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    @Autowired
    private UserService userService;
    @Autowired
    private WeixinPayConfigService weixinPayConfigService;
    @Autowired
    private LoginLogService loginLogService;
    @Autowired
    private RideCardUserService rideCardUserService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private AgentDepositVipService agentDepositVipService;
    @Autowired
    private UserToAgentDao userToAgentDao;
    @Autowired
    private GeoService geoService;
    @Autowired
    private VipCardLogService vipCardLogService;
    @Autowired
    private AccountPhoneLoginService accountPhoneLoginService;
    @Autowired
    private InviteNewEventService inviteNewEventService;
    @Autowired
    private UserStaService userStaService;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private AliConfigService aliConfigService;
    @Autowired
    private ZfbPayConfigService zfbPayConfigService;
    @Autowired
    private RideLogService rideLogService;
    @Autowired
    private RechargeService rechargeService;
    @Autowired
    private CardAccountBoundService cardAccountBoundService;
    @Autowired
    private UserWxzffService userWxzffService;
    @Autowired
    private UserOtherBoundService userOtherBoundService;
    @Autowired
    private ZhifubaoPayService zhifubaoPayService;
    @Autowired
    private OtherLoginService otherLoginService;
    @Autowired
    private UserFaceLogService userFaceLogService;
    @Resource
    private UserServiceImpl userServiceImpl;
    @Resource
    private RefundSwitchConfig refundSwitchConfig;
    @Resource
    private UserOpLogService userOpLogService;
    @Resource
    private CouponUserService couponUserService;
    @Resource
    private GiftCardUserService giftCardUserService;
    @Autowired
    private UniAppMenuService uniAppMenuService;
    @Autowired
    private MemberLogService memberLogService;
    @Autowired
    private BrandShareRelationService brandShareRelationService;

    /**
     * 短信验证码登录
     *
     * @param accountId  品牌id
     * @param phone      电话
     * @param authCode   验证码
     * @param lon        经度
     * @param lat        纬度
     * @param mapType    地图类型
     * @param inviteCode 邀请码
     * @param session    会话
     * @param response   响应
     * @ignoreParams request
     */
    @RequestMapping("/login")
    public void login(Integer accountId, String phone, String authCode, Double lon, Double lat, Integer mapType, String inviteCode,
                      HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            /*查询用户信息*/
            User user = userService.getByAccountIdAndPhone(accountId, phone);
            loginValid(accountId,phone);
            /* 校验 */
            Assert.notNull(accountId, "-10001", "账户Id不能为空");
            Assert.hasLength(phone, "-20001", "手机号码不能为空");
            Assert.hasLength(authCode, "-20002", "验证码不能为空");
            long startTime = System.currentTimeMillis();
            if (SystemConstant.login_button) {
                throw new BaseException("-2014", "当前使用人数较多，请稍后再试");
            }
            //获取ip地址
            String clientIP = ServletUtil.getClientIP(request);
            //检查是否被锁定,防止暴力破解
            checkCodeLock(phone, clientIP);
            String totpCode = null;
            String preTOTPCode = null;
            try {
                //兼容六位和四位超级验证码的情况
                if (authCode.length() > 4) {
                    //当前时间的动态口令
                    totpCode = GoogleAuthenticatorUtils.getTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET_PLUS, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS_PLUS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE_PLUS));
                    //最近刚过期的动态口令
                    preTOTPCode = GoogleAuthenticatorUtils.getPreTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET_PLUS, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS_PLUS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE_PLUS));
                } else {
                    //当前时间的动态口令
                    totpCode = GoogleAuthenticatorUtils.getTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE));
                    //最近刚过期的动态口令
                    preTOTPCode = GoogleAuthenticatorUtils.getPreTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE));
                }
            } catch (Exception e) {
                //获取动态口令失败
                log.error("获取动态口令失败，当前输入手机号{}，当前输入验证码{}", phone, authCode);
                log.error(e.getMessage(), e);
            }
            /**用对苹果审核*/
            if (!authCode.equals(totpCode) && !authCode.equals(preTOTPCode)) {
                /*获取验证码*/
                String authCodeSession = redisService.get(phone);
                if (authCodeSession == null) {
                    preventBruteForceCracking(phone, clientIP);
                    throw new BaseException("-20003", "请先发送验证码");
                }

                /*判断验证码是否正确*/
                if (!authCodeSession.equals(authCode)) {
                    //防止暴力破解超级密码
                    preventBruteForceCracking(phone, clientIP);
                    throw new BaseException("-20004", "验证码错误");
                }

                /*清空redis信息*/
                redisService.del(phone);
            }
            if (mapType == null) {
                mapType = MapConstant.MAP_GOOGLE;
            }
            user = handle(user, accountId, phone, lon, lat, mapType, request, inviteCode);
            logger.warn("用户" + phone + "验证码登录，耗时" + (System.currentTimeMillis() - startTime) + "ms");
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, user);
            //登录成功，去掉错误计数key
            redisService.del(RedisKeyConstant.getLockCountKey(phone, clientIP));
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 微信授权登录
     *
     * @param accountId     品牌id
     * @param code          代码
     * @param encryptedData 加密数据
     * @param iv            iv
     * @param lon           经度
     * @param lat           纬度
     * @param inviteCode    邀请码
     * @param mapType       地图类型
     * @param session       会话
     * @param response      响应
     * @ignoreParams request
     */
    @RequestMapping("/wxLogin")
    public void wxLogin(Integer accountId, String code, String encryptedData, String iv, Double lon, Double lat, String inviteCode, Integer mapType,
                        HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        log.info("wxLogin="+code);
        try {
            long startTime = System.currentTimeMillis();
            /* 校验 */
            Assert.notNull(accountId, "-10001", "账户Id不能为空");
            Assert.hasLength(code, "-1007", "微信授权登录参数不能为空");
            Assert.hasLength(encryptedData, "-1007", "微信授权登录参数不能为空");
            Assert.hasLength(iv, "-1007", "微信授权登录参数不能为空");
            if (SystemConstant.login_button) {
                throw new BaseException("-2014", "当前使用人数较多，请稍后再试");
            }
            /**查询微信配置信息*/
            WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByAccountId(accountId, 2);
            if (weixinPayConfig == null) {
                throw new BaseException("-1006", "微信小程序信息暂未配置");
            }
            if (mapType == null) {
                mapType = MapConstant.MAP_GOOGLE;
            }
            StringBuffer stringBuffer = new StringBuffer();
            /**获取用户手机号码*/
            String phone = WxMPUtil
                    .decryptionUserPhone(weixinPayConfig.getAppId(), weixinPayConfig.getSecret(), code, encryptedData, iv, stringBuffer);
            logger.warn("获取用户手机号码" + phone + "耗时" + (System.currentTimeMillis() - startTime) + "ms");
            if (phone == null) {
                throw new BaseException("-1005", "微信授权登录失败");
            }
            /**微信支付宝授权登录限制带+号和非11位手机号码*/
            if (phone.contains("+") || phone.length() != 11) {
                throw new BaseException("-3070", "微信授权手机号非法，限制登录");
            }
            /*查询用户信息*/
            User user = userService.getByAccountIdAndPhone(accountId, phone);
            loginValid(accountId,phone);

            user = handle(user, accountId, phone, lon, lat, mapType, request, inviteCode);
            /**校验实名认证用户是否在其他品牌已经实名*/
            brandShareRelationService.handleNameAuthByBrandShare(user);

            /**处理openid*/
            UserWxzff userWxzff = new UserWxzff();
            userWxzff.setOpenId(stringBuffer.toString());
            userWxzff.setAccountId(user.getAccountId());
            userWxzff.setUserId(user.getUserId());
            userWxzff.setAccountId(accountId);
            //5.0.0 修复空指针
            userWxzff.setUpdateTime(new Date());
            userWxzffService.add(userWxzff);
            /**记录用户第三方唯一id*/
            UserOtherBound userOtherBound = new UserOtherBound(user.getUserId(), stringBuffer.toString(), accountId, RechargeConstant.WXZFF_AUTH, UserConstant.OTHER_TYPE_WX);
            userOtherBoundService.insert(userOtherBound);
            logger.warn("用户" + phone + "微信授权登录，耗时" + (System.currentTimeMillis() - startTime) + "ms");
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, user);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取个人信息
     *
     * @param token       令牌
     * @param adAccountId 区域id
     * @param session     会话
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/getByUserId")
    public void getByUserId(String token, Integer adAccountId, HttpSession session,
                            HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            User userDB = userService.getByUserId(user.getUserId());
            if (adAccountId == null) {
                UserToAgent userToAgent = userToAgentDao.getByUserId(user.getUserId());
                if (userToAgent != null) {
                    adAccountId = userToAgent.getAccountId();
                }
            }

            if (userDB != null && adAccountId != null) {
                List<Integer> accountIds = cardAccountBoundService.getAccountId(adAccountId, user.getAccountId(),
                        CardTypeConstant.TYPE_RIDECARD);
                AgentDepositVip agentDepositVip = agentDepositVipService.getByUserId(user.getUserId(), adAccountId);
                if (agentDepositVip != null && accountIds.contains(agentDepositVip.getAccountId()) && DateTimeUtil.isDepositValid(agentDepositVip.getExpireDt())) {
                    userDB.setDepositDate(agentDepositVip.getExpireDt());
                }
                List<VipCardLog> vipCardLogs = vipCardLogService.getByTime(adAccountId, user.getUserId());
                for (VipCardLog vipCardLog : vipCardLogs) {
                    if (vipCardLog != null && accountIds.contains(vipCardLog.getAccountId()) && DateTimeUtil.isDepositValid(vipCardLog.getExpireDate())) {
                        userDB.setVipDate(vipCardLog.getExpireDate());
                    }
                }
                adAccountId = null;
                List<RideCardUser> rideCardUsers = rideCardUserService.getByUserId(user.getUserId(), adAccountId);
                rideCardUsers.forEach(rideCardUser -> {
                    if (rideCardUser != null && accountIds.contains(rideCardUser.getAccountId()) && DateTimeUtil.isDepositValid(rideCardUser.getEndDate())) {
                        userDB.setRideCardDate(rideCardUser.getEndDate());
                    }
                });

                UserWxzff userWxzff = userWxzffService.getByUserId(user.getUserId());
                //兼容以前逻辑，不存在则返回0
                String openId = "0";
                if (userWxzff != null) {
                    openId = userWxzff.getOpenId();
                }
                userDB.setOpenId(openId);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            desensitization(userDB);
            map.put(ResponseCode.DATA, userDB);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 编辑个人信息
     *
     * @param token    令牌
     * @param user     用户
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/update")
    public void update(String token, User user, HttpSession session, HttpServletRequest request,
                       HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User userSession = tokenService.getUser(token);

            User userDB = userService.getByUserId(userSession.getUserId());

            if (user.getBirthDay() != null) {
                userDB.setBirthDay(user.getBirthDay());
            }
            if (user.getEmail() != null) {
                userDB.setEmail(user.getEmail());
            }
            if (user.getSex() != null) {
                userDB.setSex(user.getSex());
            }

            userService.update(userDB);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            desensitization(userDB);
            map.put(ResponseCode.DATA, userDB);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 实名认证
     *
     * @param isEncrypt  姓名和身份证是否加密（兼容旧版，新版客户端会传true）
     * @param token    令牌
     * @param idNO     idNO
     * @param name     名称
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/nameAuth")
    public void nameAuth(@RequestParam(defaultValue = "false") Boolean isEncrypt,String token, String idNO, String name, Integer operationAreaId, HttpSession session,
                         HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();

        try {
            User userSession = tokenService.getUser(token);

            Assert.hasLength(idNO, "-3022", "身份证号码不能为空");
            Assert.hasLength(name, "-3023", "真实姓名不能为空");
            if (isEncrypt) {
                name = Base64.decodeStr(Base64.decodeStr(name));
                idNO = Base64.decodeStr(Base64.decodeStr(idNO));
            }
            userService.authName(userSession.getUserId(), userSession.getAccountId(), idNO, name, operationAreaId);

            map.put(ResponseCode.RET, ResponseCode.SUCC);

            // 插入用户操作日志
            userOpLogService.insertSingle(UserOpLog.builder()
                    .accountId(userSession.getAccountId())
                    .opUser(userSession.getName())
                    .opUserPhone(userSession.getPhone())
                    .targetUser(userSession.getName())
                    .targetUserPhone(userSession.getPhone())
                    .operation("用户账号")
                    .opType("实名认证")
                    .opPlatform(OperationPlatform.CLIENT.code)
                    .opTime(LocalDateTime.now())
                    .result(true)
                    .build());
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 心跳
     *
     * @param @param   session 会话
     * @param response 响应
     * @ignoreParams request
     * @ignore
     */
    @RequestMapping("/heartbeat")
    public void heartbeat(HttpSession session, HttpServletRequest request,
                          HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);

        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 检查令牌
     *
     * @param @param   token 令牌
     * @param session  会话
     * @param response 响应
     * @ignoreParams request
     * @ignore
     */
    @RequestMapping("/checkToken")
    public void checkToken(String token, HttpSession session, HttpServletRequest request,
                           HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            Assert.hasLength(token, "-1001", "token不能为空");

            Boolean valid = tokenService.getUser(token) == null ? false : true;

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, valid);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取用户信息
     *
     * @param token    令牌
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/getUserInfo")
    public void getUserInfo(String token, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);

            User userDB = userService.getByUserId(user.getUserId());
            if (userDB != null) {
                /**获取骑行卡到期时间*/
                List<RideCardUser> rideCardUsers = rideCardUserService.getByValid(userDB.getUserId());
                if (rideCardUsers.size() > 0) {
                    userDB.setRideCardDate(rideCardUsers.get(0).getEndDate());
                }
            }

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            desensitization(userDB);
            map.put(ResponseCode.DATA, userDB);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 上传openId
     *
     * @param token    令牌
     * @param code     微信code
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/setOpenId")
    public void setOpenId(String token, String code, HttpServletRequest request,
                          HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            userService.setOpenId(code, user, user.getAccountId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 校验是否曾经登录
     *
     * @param code
     * @param accountId
     * @param request
     * @param response
     */
    @RequestMapping("/checkLogin")
    public void checkLogin(String code, Integer accountId, HttpServletRequest request,
                           HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            log.info("checkLogin="+code);
            List<User> users = userService.checkLogin(code, accountId);
            for (User user : users) {
                desensitization(user);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, users);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * zfb登录
     *
     * @param accountId 品牌id
     * @param sign      标志
     * @param content   内容
     * @param code      支付宝code
     * @param lon       经度
     * @param lat       纬度
     * @param mapType   地图类型
     * @param response  响应
     * @ignoreParams request
     */
    @RequestMapping("/zfbLogin")
    public void zfbLogin(Integer accountId, String sign, String content, String code, Double lon, Double lat, Integer mapType, String inviteCode,
                         HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        long startTime = System.currentTimeMillis();
        try {
            if (SystemConstant.login_button) {
                throw new BaseException("-2014", "当前使用人数较多，请稍后再试");
            }
            /**支付配置信息*/
            ZfbPayConfig zfbPayConfig = zfbPayConfigService.getByAccountId(accountId, RechargeConstant.ZFB_PAY_XCX);
            if (zfbPayConfig == null) {
                throw new BaseException("-2009", "该品牌支付宝未配置");
            }
            //获取支付宝信息
            AlipaySystemOauthTokenResponse reponse = aliConfigService.alipaySystemOauthToken(accountId, code, zfbPayConfig);
            String aliUserId = reponse.getUserId();

            //获取支付宝手机号
            String phone = aliConfigService.decryptPhone(accountId, sign, content, zfbPayConfig);
            if (phone == null) {
                throw new BaseException("-170004", "支付宝授权登录失败");
            }
            /**微信支付宝授权登录限制带+号和非11位手机号码*/
            if (phone.contains("+") || phone.length() != 11) {
                throw new BaseException("-3070", "微信授权手机号非法，限制登录");
            }
            User user = userService.getByAccountIdAndPhone(accountId, phone);
            loginValid(accountId,phone);
            user = handle(user, accountId, phone, lon, lat, mapType, request, inviteCode);
            /**获取用户身份信息*/
            zhifubaoPayService.getAlipayUserInfo(reponse.getAccessToken(), user, zfbPayConfig);
            /**校验实名认证用户是否在其他品牌已经实名*/
            brandShareRelationService.handleNameAuthByBrandShare(user);

            /**记录用户第三方唯一id*/
            UserOtherBound userOtherBound = new UserOtherBound(user.getUserId(), aliUserId, accountId, RechargeConstant.WXZFF_AUTH, UserConstant.OTHER_TYPE_ZFB);
            userOtherBoundService.insert(userOtherBound);
            /**支付宝id存入内存*/
            SystemData.aliPayMap.put(user.getUserId(), aliUserId);
            logger.warn("用户" + user.getPhone() + "支付宝授权登录，耗时" + (System.currentTimeMillis() - startTime) + "ms");
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, user);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 网易易盾一键登录
     *
     * @param accountId   帐户id
     * @param token       令牌
     * @param accessToken 访问令牌
     * @param lon         经度
     * @param lat         纬度o
     * @param mapType     地图类型
     * @param session     会话
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/oneLogin")
    public void oneLogin(Integer accountId, String token, String accessToken, Double lon, Double lat, Integer mapType, String inviteCode,
                         HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            long startTime = System.currentTimeMillis();
            /* 校验 */
            Assert.notNull(accountId, "-10001", "账户Id不能为空");
            Assert.hasLength(token, "-170006", "token不能为空");
            Assert.hasLength(accessToken, "-170007", "accessToken不能为空");

            if (mapType == null) {
                mapType = MapConstant.MAP_GOOGLE;
            }
            AccountPhoneLogin accountPhoneLogin = accountPhoneLoginService.getByAccountId(accountId);
            if (accountPhoneLogin == null) {
                throw new BaseException("-170009", "一键登录未配置");
            }
            /**获取一键登录手机号码*/
            String phone = WyydLoginUtil.getPhone(token, accessToken, accountPhoneLogin);
            if (phone == null) {
                throw new BaseException("-170008", "一键登录获取手机号码失败");
            }
            /**微信支付宝授权登录限制带+号和非11位手机号码*/
            if (phone.contains("+") || phone.length() != 11) {
                throw new BaseException("-3070", "授权获取手机号非法，限制登录");
            }
            /*查询用户信息*/
            User user = userService.getByAccountIdAndPhone(accountId, phone);
            loginValid(accountId,phone);
            user = handle(user, accountId, phone, lon, lat, mapType, request, inviteCode);
            logger.warn("用户" + phone + "一键登录，耗时" + (System.currentTimeMillis() - startTime) + "ms");

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, user);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 第三方登录
     *
     * @param accountId 品牌id
     * @param phone     手机号码
     * @param sign      签名
     * @param time      时间戳
     * @param session
     * @param request
     * @param response
     */
    @RequestMapping("/otherLogin")
    public void otherLogin(Integer accountId, String phone, String sign, Long time,
                           HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            long startTime = System.currentTimeMillis();
            /* 校验 */
            Assert.notNull(accountId, "-10001", "账户Id不能为空");
            Assert.hasLength(phone, "-20001", "手机号码不能为空");
            boolean result = MD5PasswordUtil.maasCheck(SystemConstant.MAAS_SECRET, sign, time, phone + accountId);
            if (!result) {
                throw new BaseException("-190002", "签名校验失败");
            }
            /**第三方登录手机号码添加前缀*/
            phone = "MAAS" + phone;
            /*查询用户信息*/
            User user = userService.getByAccountIdAndPhone(accountId, phone);
            loginValid(accountId,phone);
            user = handle(user, accountId, phone, null, null, null, request, null);
            /**第三方用户更新押金状态为虚拟押金*/
            userService.updateDeposit(user.getUserId(), DepositStateConstant.DEPOSIT_STATE_VIRTUAL);
            logger.warn("用户" + phone + "第三方登录，耗时" + (System.currentTimeMillis() - startTime) + "ms");

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, user);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 第三方检验登录状态
     *
     * @param token
     * @param session
     * @param request
     * @param response
     */
    @RequestMapping("/otherCheck")
    public void otherCheck(String token,
                           HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {

            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取H5登录签名
     *
     * @param accountId 品牌id
     * @param session
     * @param request
     * @param response
     */
    @RequestMapping("/getH5Sign")
    public void getSign(Integer accountId,
                        HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            Assert.notNull(accountId, "-10001", "平台Id不能为空");
            String result = otherLoginService.getSign(accountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * H5登录
     *
     * @param accessToken     鉴权token
     * @param userInformation 浏览器加密指纹
     * @param accountId       品牌id
     * @param session
     * @param request
     * @param response
     */
    @RequestMapping("/H5Login")
    public void getPhone(String accessToken, String userInformation, Integer accountId,
                         HttpSession session, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            Assert.notNull(accountId, "-10001", "平台Id不能为空");
            Assert.notNull(accountId, "-10003", "accessToken不能为空");
            Assert.notNull(accountId, "-10004", "userInformation不能为空");
            String phone = otherLoginService.getPhone(accessToken, userInformation, accountId);
            if (phone == null) {
                throw new BaseException("-3070", "授权获取手机号非法，限制登录");
            }
            User user = userService.getByAccountIdAndPhone(accountId, phone);
            loginValid(accountId,phone);
            user = handle(user, accountId, phone, null, null, null, request, null);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, user);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 登录完成数据处理
     *
     * @param user
     * @param accountId
     * @param phone
     * @param lon
     * @param lat
     * @param mapType
     * @param request
     */
    public User handle(User user, Integer accountId, String phone, Double lon, Double lat, Integer mapType,
                       HttpServletRequest request, String inviteCode) {
        if (user == null) {
            /**兼容新用户赠送功能*/
            Integer agentId = null;
            if (lon != null && lat != null) {
                agentId = geoService.getBelongAccount(accountId, lon, lat, mapType);
            }
            /*添加用户*/
            user = new User();
            user.setAccountId(accountId);
            user.setBirthDay(DateTimeUtil.getNowTimeyyyyMMdd());
            user.setEmail("");
            user.setPhone(phone);
            user.setRemark("");
            user.setSex("");
            user.setUpdateTime(DateTimeUtil.getNowTime());
            user.setJoinTime(DateTimeUtil.getNowTime());
            if (user.getCountry() == null) {
                user.setCountry("86");
            }
            userService.insert(user, agentId);

            /**获取用户userId*/
            user = userService.getByAccountIdAndPhone(accountId, phone);
        }

        //存储到缓存中并返回对应的token
        String token = tokenService.storeWithToken(user);
        user.setToken(token);

        /**登录日志*/
        String ip = IpUtil.getClientIp(request);
        String userAgent = request.getHeader("User-Agent");
        if (userAgent == null) {
            userAgent = "";
        }

        LoginLog loginLog = new LoginLog();
        loginLog.setType(UserConstant.USER_USER);
        loginLog.setId(user.getUserId());
        loginLog.setIp(ip);
        loginLog.setLoginTime(DateTimeUtil.getNowTime());
        loginLog.setAgent(userAgent);

        loginLogService.insert(loginLog);

        // 插入用户操作日志
        userOpLogService.insertSingle(UserOpLog.builder()
                .accountId(user.getAccountId())
                .opUser(user.getName())
                .opUserPhone(user.getPhone())
                .targetUser(user.getName())
                .targetUserPhone(user.getPhone())
                .operation("用户账号")
                .opType("登录")
                .opPlatform(OperationPlatform.CLIENT.code)
                .opTime(LocalDateTime.now())
                .result(true)
                .build());

        /**新用户邀请活动*/
        if (inviteCode != null && !inviteCode.equals("")) {
            logger.info(String.format("用户邀请码登录----%s,%s", inviteCode, user.getUserId()));
            inviteNewEventService.invite(inviteCode, user);
        }
        /***/
        user.setIdNO(null);
        user.setName(null);
        desensitization(user);
        return user;
    }

    /**
     * 人脸识别E政通
     *
     * @param token
     * @param userCode 车辆编号
     * @param request
     * @param response
     */
    @RequestMapping("/faceCheck")
    public void faceCheck(String token, String userCode, HttpServletRequest request,
                          HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            User user = tokenService.getUser(token);
            String eToken = userService.faceCheck(user, userCode);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, eToken);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 人脸识别腾讯
     *
     * @param token
     * @param userCode
     * @param redirectUrl
     * @param request
     * @param response
     */
    @RequestMapping("/faceCheckTencent")
    public void faceCheckTencent(String token, String userCode, String redirectUrl, HttpServletRequest request,
                                 HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Assert.hasLength(redirectUrl, "-30011", "回调地址不能为空");
            User user = tokenService.getUser(token);
            DetectAuthResponse bizToken = userService.faceCheckTencent(user, userCode, redirectUrl);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, bizToken);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 优驱实名认证+人脸识别认证
     * @return Result
     */
    @PostMapping("/nameAuthGetBizToken")
    public void nameAuthGetBizToken(@RequestParam(defaultValue = "false") Boolean isEncrypt, String token, String redirectUrl, String userName, String idNO, Integer accountId, HttpServletRequest request,
                                    HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.hasLength(idNO, "-3022", "身份证号码不能为空");
            Assert.hasLength(userName, "-3023", "真实姓名不能为空");
            if (StrUtil.isEmpty(redirectUrl)){
                throw new BaseException("-30010", "优驱腾讯人脸回调地址不能为空");
            }
            if (isEncrypt) {
                userName = Base64.decodeStr(Base64.decodeStr(userName));
                idNO = Base64.decodeStr(Base64.decodeStr(idNO));
            }
            User user = tokenService.getUser(token);
            DetectAuthResponse detectAuthResponse = userService.nameAuthGetBizToken(user, accountId, redirectUrl, userName, idNO);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, detectAuthResponse);
        }catch (BaseException e){
            log.error("实名认证+人脸识别认证异常:", e);
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 身份证+人脸识别认证 获取结果
     */
    @PostMapping("/nameAuthGetResult")
    public void nameAuthGetResult(String token, String bizToken, Integer accountId, HttpServletRequest request,
                                  HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            if (Objects.isNull(accountId)){
                throw new BaseException("请选择区域");
            }
            Assert.hasLength(bizToken, "-30010", "bizToken不能为空");
            User user = tokenService.getUser(token);
            Boolean result = userService.nameAuthGetResult(user, bizToken, accountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        }catch (BaseException e){
            log.error("优驱腾讯云人脸识别失败:", e);
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }



    /**
     * 优驱腾讯云人脸识别
     * @param token token
     * @param userCode 车辆编号
     * @param redirectUrl 回调地址
     * @return result
     */
    @RequestMapping("/yqFaceTencent")
        public void yqFaceTencent(String token, String userCode, String redirectUrl, Integer accountId, HttpServletRequest request,
                                        HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Assert.hasLength(redirectUrl, "-30011", "回调地址不能为空");
            User user = tokenService.getUser(token);
            DetectAuthResponse bizToken = userService.youquFaceCheckTencent(user, userCode, redirectUrl, accountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, bizToken);
        } catch (BaseException e) {
            log.error("优驱腾讯云人脸识别失败:", e);
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取优驱腾讯人脸识别结果
     * @param token token
     * @param bizToken bizToken
     * @param userCode 车辆编号
     * @param accountId 区域id
     * @return result
     */
    @RequestMapping("/getuqResult")
    public void getuqResult(String token, String bizToken, String userCode, Integer accountId, HttpServletRequest request,
                            HttpServletResponse response){
        Map<String, Object> map = new HashMap<String, Object>();
        User user = new User();
        Machine machine = new Machine();
        boolean result = false;
        FaceCheck faceCheck = null;
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Assert.hasLength(bizToken, "-30010", "bizToken不能为空");
            user = tokenService.getUser(token);
            machine = userServiceImpl.machineCheck(userCode);
            //校验人脸识别上限
            faceCheck = userServiceImpl.faceTimesCheck(user.getUserId());
            result = userService.getuqResult(user, bizToken, machine, accountId);
            userServiceImpl.faceCheckAdd(result, faceCheck, user, machine.getAccountId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        }catch (Exception e){
            log.error("获取人脸结果失败",e);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取腾讯人脸识别结果
     *
     * @param token
     * @param bizToken bizToken
     * @param request
     * @param response
     */
    @RequestMapping("/getTencentResult")
    public void getTencentResult(String token, String bizToken, String userCode, HttpServletRequest request,
                                 HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        User user = new User();
        Machine machine = new Machine();
        FaceCheck faceCheck = null;
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Assert.hasLength(bizToken, "-30010", "bizToken不能为空");
            user = tokenService.getUser(token);
            machine = userServiceImpl.machineCheck(userCode);
            //校验人脸识别上限
            faceCheck = userServiceImpl.faceTimesCheck(user.getUserId());
            boolean result = userService.getTencentResult(user, bizToken, machine);
            userServiceImpl.faceCheckAdd(result, faceCheck, user, machine.getAccountId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取yq人脸识别自研结果
     *
     * @param token
     * @param bizToken bizToken
     * @param request
     * @param response
     */
    @RequestMapping("/getTencentResultLocal")
    public void getTencentResultLocal(String token, String bizToken, String userCode, HttpServletRequest request,
                                 HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        User user = new User();
        Machine machine = new Machine();
        FaceCheck faceCheck = null;
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Assert.hasLength(bizToken, "-30010", "bizToken不能为空");
            user = tokenService.getUser(token);
            machine = userServiceImpl.machineCheck(userCode);
            //校验人脸识别上限
            faceCheck = userServiceImpl.faceTimesCheck(user.getUserId());
            boolean result = userService.getTencentResultLoacl(user, bizToken, machine);
            userServiceImpl.faceCheckAdd(result, faceCheck, user, machine.getAccountId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 阿里云人脸识别
     *
     * @param token     令牌
     * @param base64Str 人脸图片 base64 str
     * @param userCode  车辆编号
     * @param response  响应
     * @ignoreParams request
     */
    @RequestMapping("/faceCheckNew")
    public void faceCheckNew(String token, String base64Str, String userCode,
                             HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>(8);
        User user = new User();
        Machine machine = new Machine();
        FaceCheck faceCheck = null;
        try {
            Assert.hasLength(base64Str, "-20011", "请上传人脸图片");
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            user = tokenService.getUser(token);
            machine = userServiceImpl.machineCheck(userCode);
            //校验人脸识别上限
            faceCheck = userServiceImpl.faceTimesCheck(user.getUserId());
            userService.faceCheckNew(user, base64Str, machine);
            userServiceImpl.faceCheckAdd(true, faceCheck, user, machine.getAccountId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            String message = e.getMessage();
            if (message != null && message.contains("%")) {
                String[] array = message.split("%");
                //人脸识别失败
                if (array[0] != null && array[0].equals("-20008")) {
                    userServiceImpl.faceCheckAdd(false, faceCheck, user, machine.getAccountId());
                }
            }
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }


    /**
     * 检验用户是否有快过期套餐
     *
     * @param adAccountId 区域id
     * @param token       令牌
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/expireSoon")
    public void expireSoon(Integer adAccountId, String token, HttpServletRequest request,
                           HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            userService.expireSoon(user, map, adAccountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取用户信用分信息
     *
     * @param token
     * @param request
     * @param response
     */
    @RequestMapping("/credit")
    public void credit(String token, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            UserSta userSta = userStaService.getByUserId(user.getUserId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, userSta);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 用户申请退款
     *
     * @param adAccountId 区域id
     * @param token       令牌
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/returnMoney")
    public void returnMoney(Integer adAccountId, String token, HttpServletRequest request,
                            HttpServletResponse response) {
        String refundModel = refundSwitchConfig.getRefundModel();
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            User userDB = userService.getByUserId(user.getUserId());
            if (Objects.equals(refundModel,RefundSwitchConfig.REFUND_NEW)){
                // 当前退款批次号，仅用于查询同一次请求的分账信息
                String refundBatch = System.currentTimeMillis() + String.format("%06d", ThreadLocalRandom.current().nextInt(0, 1000000));
                // 新退款接口 配置了新接口才为新退款，否则走老退款
                userService.returnMoneyNew(adAccountId, userDB.getMoney(), user.getUserId(), false, null, null, null, refundBatch);
            }else {
                // 老退款接口
                userService.returnMoney(adAccountId, userDB.getMoney(), user.getUserId(), false, null, null, null);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 用户申请退款(新)
     *
     * @param adAccountId 区域id
     * @param token       令牌
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/returnMoneyNew")
    public void returnMoneyNew(Integer adAccountId, String token, HttpServletRequest request,
                                HttpServletResponse response) {
        String refundModel = refundSwitchConfig.getRefundModel();
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            User userDB = userService.getByUserId(user.getUserId());
            if (Objects.equals(refundModel,RefundSwitchConfig.REFUND_NEW)){
                // 当前退款批次号，仅用于查询同一次请求的分账信息
                String refundBatch = System.currentTimeMillis() + String.format("%06d", ThreadLocalRandom.current().nextInt(0, 1000000));
                // 新退款接口 配置了新接口才为新退款，否则走老退款
                userService.returnMoneyNew(adAccountId, userDB.getMoney(), user.getUserId(), false, null, null, null, refundBatch);
            }else {
                // 老退款接口
                userService.returnMoney(adAccountId, userDB.getMoney(), user.getUserId(), false, null, null, null);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 人脸识别有效期上传
     *
     * @param token
     * @param adAccountId 区域id
     * @param request
     * @param response
     */
    @RequestMapping("/addFace")
    public void addFace(String token, Integer adAccountId, HttpServletRequest request,
                        HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);

            AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey(adAccountId,
                    AccountConfigExtConstant.ACCOUNTCONFIG_FACE);
            Integer second = 0;
            if (accountConfigExt != null) {
                second = Integer.valueOf(accountConfigExt.getParamValue());
            }
            redisService.add(RedisConstant.REDIS_USER_FACE + user.getUserId(), "1", second);
            logger.warn(user.getPhone() + "添加人脸有效期" + second + "秒");
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 查看新用户
     *
     * @param token
     * @param request
     * @param response
     */
    @RequestMapping("/newUserCheck")
    public void newUserCheck(String token, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);
            Integer rideLogs = rideLogService.queryCount(user.getUserId());
            map.put(ResponseCode.DATA, "false");
            if (rideLogs == 0) {
                map.put(ResponseCode.DATA, "true");
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 网页平台订单退款或者余额订单退款
     *
     * @param userId        用户id
     * @param money         金额
     * @param fineMoney     罚款金额
     * @param buyOrderNO    订单号(骑行卡会员卡免押卡、优惠券套餐 传递订单号、骑行订单传递骑行订单号)
     * @param sign          签名
     * @param date          时间戳
     * @param param         附加参数
     * @param operatorId    操作人id
     * @param operatorName  操作人姓名
     * @param operatorPhone 操作人手机号
     * @param request       请求
     * @param response      响应
     */
    @RequestMapping("/returnMoneyApi")
    public void returnMoneyApi(Integer userId, @RequestParam(required = false) Integer adAccountId, Double money, @RequestParam(required = false) Long fineMoney, String buyOrderNO, String sign, Long date, String param,
                               @RequestParam(required = false) Integer operatorId, @RequestParam(required = false) String operatorName, @RequestParam(required = false) Integer sendMsg,
                               @RequestParam(required = false) String operatorPhone, @RequestParam(required = false) String remark, HttpServletRequest request, HttpServletResponse response) {
        String refundModel = refundSwitchConfig.getRefundModel();
        Map<String, Object> map = new HashMap<String, Object>();
        double refundMoney = 0d;
        RefundMoneyReturnDTO refundMoneyReturnDTO = null;
        try {
            Assert.notNull(userId, "-20005", "用户不存在");
            Assert.notNull(money, "-20021", "请输入退款金额");
            if (Objects.equals(refundModel,RefundSwitchConfig.REFUND_NEW)){
                refundMoneyReturnDTO = userService.returnMoneyApiNew(userId, money, fineMoney, buyOrderNO, sign, date, param, adAccountId, operatorId, operatorName, operatorPhone, sendMsg, remark);
            }else {
                refundMoney = userService.returnMoneyApi(userId, money, fineMoney, buyOrderNO, sign, date, param, adAccountId, operatorId, operatorName, operatorPhone, sendMsg, remark);
                refundMoneyReturnDTO = new RefundMoneyReturnDTO(refundMoney,null);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, JSON.toJSONString(refundMoneyReturnDTO));
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 网页平台订单退款或者余额订单退款（退款新）
     *
     * @param userId        用户id
     * @param money         金额
     * @param fineMoney     罚款金额
     * @param buyOrderNO    订单号(骑行卡会员卡免押卡、优惠券套餐 传递订单号、骑行订单传递骑行订单号)
     * @param sign          签名  A0927CD7314E8C8380413D284390CF68
     * @param date          时间戳 1712741557574
     * @param param         附加参数    15079017029267
     * @param operatorId    操作人id
     * @param operatorName  操作人姓名
     * @param operatorPhone 操作人手机号
     * @param request       请求
     * @param response      响应
     */
    @RequestMapping("/returnMoneyApiNew")
    public void returnMoneyApiNew(Integer userId, @RequestParam(required = false) Integer adAccountId, Double money, @RequestParam(required = false) Long fineMoney, String buyOrderNO, String sign, Long date, String param,
                                   @RequestParam(required = false) Integer operatorId, @RequestParam(required = false) String operatorName, @RequestParam(required = false) Integer sendMsg,
                                   @RequestParam(required = false) String operatorPhone, @RequestParam(required = false) String remark, HttpServletRequest request, HttpServletResponse response) {


        String refundModel = refundSwitchConfig.getRefundModel();
        Map<String, Object> map = new HashMap<String, Object>();
        double refundMoney = 0d;
        RefundMoneyReturnDTO refundMoneyReturnDTO = null;
        try {
            Assert.notNull(userId, "-20005", "用户不存在");
            Assert.notNull(money, "-20021", "请输入退款金额");
            if (Objects.equals(refundModel,RefundSwitchConfig.REFUND_NEW)){
                refundMoneyReturnDTO = userService.returnMoneyApiNew(userId, money, fineMoney, buyOrderNO, sign, date, param, adAccountId, operatorId, operatorName, operatorPhone, sendMsg, remark);
            }else {
                //该接口已经不在使用
                refundMoney = userService.returnMoneyApi(userId, money, fineMoney, buyOrderNO, sign, date, param, adAccountId, operatorId, operatorName, operatorPhone, sendMsg, remark);
                refundMoneyReturnDTO = new RefundMoneyReturnDTO(refundMoney,null);
            }
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, JSON.toJSONString(refundMoneyReturnDTO));
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 优驱人脸识别自研
     * @param token token
     * @param accountId 区域id
     * @return result
     */
    @RequestMapping("/yqFaceLocal")
    public void yqFaceLocal(String token,  String userCode, Integer accountId, HttpServletRequest request,
                              HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            User user = tokenService.getUser(token);
            DetectAuthResponse bizToken = userService.youquFaceCheckLocal(user, userCode,  accountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, bizToken);
        } catch (BaseException e) {
            log.error("优驱人脸识别自研:", e);
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 用户支付事件
     *
     * @param token
     * @param session
     * @param request
     * @param response
     */
    @RequestMapping("/payEvent")
    public void payEvent(String token, HttpSession session, HttpServletRequest request,
                         HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();

        try {
            User user = tokenService.getUser(token);

            userService.payEvent(user.getUserId());
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取用户实时位置
     * @param token token
     * @param userCode 车辆编号
     */
    @GetMapping("/ridingPositionDisplay")
    public void ridingPositionDisplay(String token, String userCode,  HttpServletRequest request, HttpServletResponse response){
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.hasLength(userCode, "-30006", "车辆编号不能为空");
            Integer type = userService.ridingPositionDisplay(userCode);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, type);
        }catch (BaseException e){
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }


    /**
     * 防止暴力破解超级密码 (一个账号30分钟内短信验证码连续错误3次锁定10分钟)
     *
     * @param phone    当前登录的手机号码
     * @param clientIP 当前登录的ip地址
     */
    void preventBruteForceCracking(String phone, String clientIP) {
        try {
            String lockTimeKey = RedisKeyConstant.getLockTimeKey(phone, clientIP);
            String key = RedisKeyConstant.getLockCountKey(phone, clientIP);
            String value = redisService.get(key);
            if (StringUtils.isEmpty(value)) {
                //保存三十分钟
                redisService.add(key, "1", 30 * 60);
            } else {
                Integer count = Integer.parseInt(value);
                redisService.add(key, (++count).toString(), 30 * 60);
                if (count >= 3) {
                    //到达次数,添加锁定时间key，并且重置次数
                    log.info("{}登录频繁，ip地址为{}，已锁定10分钟", phone, clientIP);
                    redisService.add(lockTimeKey, String.valueOf(System.currentTimeMillis() + (60 * 10 * 1000)), 60 * 10);
                    redisService.add(key, "0", 30 * 60);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 检查是否被锁定
     *
     * @param phone    手机号
     * @param clientIP 请求的ip地址
     */
    void checkCodeLock(String phone, String clientIP) {
        String lockTime = redisService.get(RedisKeyConstant.getLockTimeKey(phone, clientIP));
        if (!StringUtils.isEmpty(lockTime)) {
            String msg = "操作频繁";
            try {
                msg = String.format("操作频繁，请%s后再试", DateUtil.formatBetween(Long.parseLong(lockTime) - System.currentTimeMillis(), BetweenFormatter.Level.SECOND));
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            throw new BaseException("-102", msg);
        }
    }

    /**
     * 用户基本信息脱敏
     *
     * @param user
     */
    void desensitization(User user) {
        if (user != null) {
            //信息脱敏
            user.setName(DesensitizedUtil.chineseName(user.getName()));
            user.setIdNO(DesensitizedUtil.idCardNum(user.getIdNO(), 1, 2));
            //user.setPhone(DesensitizedUtil.mobilePhone(user.getPhone()));
            user.setPhone(user.getPhone().substring(0, 7) + "****");
            user.setBirthDay(DesensitizedUtil.password(user.getBirthDay()));
            user.setEmail(DesensitizedUtil.email(user.getEmail()));
        }
    }

    /**
     * 用户注销前置校验
     */
    @GetMapping("/logoutValid")
    public void logoutValid(Integer userId,HttpServletRequest request, HttpServletResponse response){
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            List<UserLogoutValidVO> validResult = new ArrayList<>();
            // 校验账号下订单是否完成
            validResult.addAll(validOrderFinish(userId));
            // 校验账号资产是否结清
            validResult.addAll(validAssetsSettled(userId));
            // 账户安全状态，授权和绑定关系
            validResult.add(new UserLogoutValidVO(UserLogoutValidVO.ACCOUNT_SAFE,UserLogoutValidVO.SUCCESS,null));
            validResult.add(new UserLogoutValidVO(UserLogoutValidVO.AUTH_BIND_LIFT,UserLogoutValidVO.SUCCESS,null));
            // 返回用户手机号
            User user = userService.getByUserId(userId);
            validResult.add(new UserLogoutValidVO(UserLogoutValidVO.PHONE,user.getPhone(),null));
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, validResult);
        }catch (BaseException e){
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 校验用户资产是否已结清
     * @param userId
     * @return
     */
    private List<UserLogoutValidVO> validAssetsSettled(Integer userId){
        List<UserLogoutValidVO> userLogoutValidVOS = new ArrayList<>();
        StringBuilder msgBuilder = new StringBuilder();
        // 用户余额
        User user = userService.getByUserId(userId);
        List<GiftCardUser> listByUserId = giftCardUserService.getListByUserId(userId);
        long count = listByUserId.stream().filter(x -> x.getMoney() > 0).count();
        if (user.getMoney() > 0 || count > 0){
            msgBuilder.append("，存在余额未消费");
        }
        // 押金
        if (user.getDepositMoney() > 0){
            msgBuilder.append("，存在押金未退还");
        }
        // 骑行卡
        if (rideCardUserService.hasValidRideCard(userId)){
            msgBuilder.append("，存在骑行卡未到期");
        }
        // 会员卡
        if (vipCardLogService.hasValidVipCard(userId)){
            msgBuilder.append("，会员卡未到期");
        }
        // 优惠券
        if (couponUserService.hasValidCoupon(userId)){
            msgBuilder.append("，优惠券未使用");
        }

        if (msgBuilder.length() == 0){
            userLogoutValidVOS.add(new UserLogoutValidVO(UserLogoutValidVO.ASSETS_SETTLED,UserLogoutValidVO.SUCCESS,null));
            return userLogoutValidVOS;
        }
        msgBuilder.delete(0,1);
        userLogoutValidVOS.add(new UserLogoutValidVO(UserLogoutValidVO.ASSETS_SETTLED,UserLogoutValidVO.FAIL,msgBuilder.toString()));
        return userLogoutValidVOS;
    }

    /**
     * 校验用户订单是否已完成
     * @param userId
     * @return
     */
    private List<UserLogoutValidVO> validOrderFinish(Integer userId){
        List<UserLogoutValidVO> userLogoutValidVOS = new ArrayList<>();
        // 骑行订单
        RideLog rideLog = rideLogService.findNOtPayByUserId(userId);
        if (Objects.nonNull(rideLog)){
            String msg = "存在未支付订单";
            if (Objects.isNull(rideLog.getEndTime())){
                msg = "存在骑行中订单";
            }
            userLogoutValidVOS.add(new UserLogoutValidVO(UserLogoutValidVO.ORDER_FINISH,UserLogoutValidVO.FAIL,msg));
            return userLogoutValidVOS;
        }
        userLogoutValidVOS.add(new UserLogoutValidVO(UserLogoutValidVO.ORDER_FINISH,UserLogoutValidVO.SUCCESS,null));
        return userLogoutValidVOS;
    }

    /**
     * 用户注销
     * @param userId 用户id
     * @param verifyCode 验证码
     */
    @PostMapping("/logout")
    public void logout(Integer userId, String verifyCode, HttpServletRequest request, HttpServletResponse response){

        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = userService.getByUserId(userId);
            String code = redisService.get(user.getPhone());
            if (!validSuperCode(verifyCode) && !Objects.equals(verifyCode,code)){
                throw new BaseException("验证码错误！");
            }
            redisService.del(user.getPhone());
            logout_modify(user);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA,true);
        }catch (BaseException e){
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 获取个人中心卡券
     * @param userId 用户id
     */
    @GetMapping("/getUserCard")
    public void getUserCard(Integer accountId, Integer userId, HttpServletRequest request, HttpServletResponse response){

        Map<String, Object> map = new HashMap<String, Object>();
        try {
            List<UserCardVO> userCardVOList = new ArrayList<>();

            String version = request.getHeader("U-App-VERSION");
            if (Objects.isNull(version)){
                version = request.getHeader("T-App-Version");
            }
            List<UniAppMenu> uniAppMenuList = uniAppMenuService.getByAccountId(accountId, version);
            Map<String, UniAppMenu> uniAppMenuMap = uniAppMenuList.stream()
                    .filter(item -> Objects.equals(item.getType(), 2))
                    .filter(item -> Objects.equals(item.getState(), 1))
                    .collect(Collectors.toMap(UniAppMenu::getName, item -> item, (k1, k2) -> k1));
            /**用户骑行卡*/
            UniAppMenu rideCardMenu = uniAppMenuMap.get(AppMenuConstant.APP_MENU_QXTC);
            if (rideCardMenu != null){
                RideCardUser rideCard = rideCardUserService.getRideCardByUserId(accountId, userId);
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_QXTC);
                userCardVO.setType(0);
                userCardVO.setName(rideCard == null ? "前往开通" : rideCard.getName());
                userCardVO.setIcon(rideCardMenu.getIcon());
                userCardVO.setShow(true);
                userCardVOList.add(userCardVO);
            }else {
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_QXTC);
                userCardVO.setType(0);
                userCardVO.setShow(false);
                userCardVOList.add(userCardVO);
            }

            /**用户会员卡*/
            UniAppMenu vipMenu = uniAppMenuMap.get(AppMenuConstant.APP_MENU_HYK);
            if (vipMenu != null){
                VipCardLog vipCard = vipCardLogService.getVipCardByUserId(accountId, userId);
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_HYK);
                userCardVO.setType(2);
                userCardVO.setName(vipCard == null ? "前往开通" : vipCard.getName());
                userCardVO.setIcon(vipMenu.getIcon());
                userCardVO.setShow(true);
                userCardVOList.add(userCardVO);
            }else {
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_HYK);
                userCardVO.setType(2);
                userCardVO.setShow(false);
                userCardVOList.add(userCardVO);
            }

            /**用户免押卡*/
            UniAppMenu depositVipMenu = uniAppMenuMap.get(AppMenuConstant.APP_MENU_MYTC);
            if (depositVipMenu != null){
                MemberLog memberLog = memberLogService.getMemberLogByUserId(userId, accountId);
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_MYTC);
                userCardVO.setType(1);
                userCardVO.setName(memberLog == null ? "前往开通" : memberLog.getMemberFeeName());
                userCardVO.setIcon(depositVipMenu.getIcon());
                userCardVO.setShow(true);
                userCardVOList.add(userCardVO);
            }else {
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_MYTC);
                userCardVO.setType(1);
                userCardVO.setShow(false);
                userCardVOList.add(userCardVO);
            }

            /**用户优惠券*/
            UniAppMenu couponMenu = uniAppMenuMap.get(AppMenuConstant.APP_MENU_WDYHQ);
            if (couponMenu != null){
                List<CouponUserDto> couponUserDtoList = couponUserService.getByUserId(accountId, userId);
                List<CouponUserDto> couponValidList = couponUserDtoList.stream().filter(c -> c.getIsValid() == 1).collect(Collectors.toList());
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_WDYHQ);
                userCardVO.setType(3);
                userCardVO.setCount(couponValidList.size());
                userCardVO.setIcon(couponMenu.getIcon());
                userCardVO.setShow(true);
                userCardVOList.add(userCardVO);
            }else {
                UserCardVO userCardVO = new UserCardVO();
                userCardVO.setName(AppMenuConstant.APP_MENU_WDYHQ);
                userCardVO.setType(3);
                userCardVO.setShow(false);
                userCardVOList.add(userCardVO);
            }

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA,userCardVOList);
        }catch (BaseException e){
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }


    /**
     * 校验是否命中超级验证码
     * @param verifyCode
     * @return
     */
    private Boolean validSuperCode(String verifyCode){
        String totpCode = null;
        String preTOTPCode = null;
        try {
            //兼容六位和四位超级验证码的情况
            if (verifyCode.length() > 4) {
                //当前时间的动态口令
                totpCode = GoogleAuthenticatorUtils.getTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET_PLUS, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS_PLUS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE_PLUS));
                //最近刚过期的动态口令
                preTOTPCode = GoogleAuthenticatorUtils.getPreTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET_PLUS, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS_PLUS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE_PLUS));
            } else {
                //当前时间的动态口令
                totpCode = GoogleAuthenticatorUtils.getTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE));
                //最近刚过期的动态口令
                preTOTPCode = GoogleAuthenticatorUtils.getPreTOTPCode(SystemConstant.SUPER_PASSWORD_AUTH_SECRET, SystemConstant.SUPER_PASSWORD_AUTH_DIGITS, Integer.parseInt(SystemConstant.SUPER_PASSWORD_AUTH_EXPIRE));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
        // 不命中超级验证码
        if (!Objects.equals(verifyCode,totpCode) && !Objects.equals(verifyCode,preTOTPCode)){
            return false;
        }
        return true;
    }

    /**
     * 假注销动作
     * @param user
     * @return
     */
    private Boolean logout_modify(User user){
        String idNO = user.getIdNO();
        if (Objects.nonNull(idNO)){
            user.setIdNO(String.format("%s-%s", idNO,user.getUserId()));
        }
        user.setPhone(String.format("%s-%s",user.getPhone(),user.getUserId()));
        user.setUpdateTime(DateTimeUtil.getNowTime());
        userService.updateAll(user);
        userWxzffService.del(user.getUserId());
        return true;
    }

    /**
     * 注册时限制三次注销
     * @param accountId 区域id
     * @param phone 手机号
     */
    private void loginValid(Integer accountId, String phone){
        Integer logoutTimes = userService.getCountByAccountIdAndPhoneNearlyYear(accountId,phone);
        if (logoutTimes > 3){
            throw new BaseException("-20055","系统检测到您的账号安全等级较低，因此暂时无法完成注册流程。如有疑问请联系客服协助解决。");
        }
    }
}